package com.gitee.mp.infra.utils;

import lombok.Getter;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationContextEvent;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author hongda.li
 */
public final class ApplicationContextUtils implements ApplicationListener<ApplicationContextEvent> {
    /**
     * Spring 上下文
     */
    @Getter
    private static ApplicationContext context;

    /**
     * Bean 实例二级缓存
     * 来源于 Spring 上下文
     */
    private static final Map<Class<?>, Object> CACHE = new ConcurrentHashMap<>();

    /**
     * 优先从缓存中获取 Bean 实例
     *
     * @param beanType 指定的 Bean 类型
     * @param <Bean>   Bean 类型
     * @return Bean 实例
     */
    public static <Bean> Bean findBeanFromCache(Class<Bean> beanType) {
        return beanType.cast(CACHE.computeIfAbsent(beanType, type -> ApplicationContextUtils.findBean(beanType)));
    }

    /**
     * 获取指定类型的 Bean 实例
     *
     * @param beanType 指定的 Bean 类型
     * @param <Bean>   Bean 类型
     * @return Bean 实例
     */
    public static <Bean> Bean findBean(Class<Bean> beanType) {
        return context.getBean(beanType);
    }

    /**
     * 获取指定类型的 Bean 实例
     *
     * @param beanType 指定的 Bean 类型
     * @param <Bean>   Bean 类型
     * @return Bean 实例
     */
    public static <Bean> Bean findBeanIfNull(Class<Bean> beanType) {
        return ApplicationContextUtils.findBeanIfNull(beanType, null);
    }

    /**
     * 获取指定类型的 Bean 实例
     *
     * @param beanType 指定的 Bean 类型
     * @param ifNull   如果无法获取指定实例时的默认返回值
     * @param <Bean>   Bean 类型
     * @return Bean 实例
     */
    public static <Bean> Bean findBeanIfNull(Class<Bean> beanType, Bean ifNull) {
        try {
            return context.getBean(beanType);
        } catch (BeansException beansException) {
            return ifNull;
        }
    }

    /**
     * 获取指定类型的所有 Bean 实例
     *
     * @param beanType 指定的 Bean 类型
     * @param <Bean>   Bean 类型
     * @return 所有 Bean 实例
     */
    public static <Bean> List<Bean> findBeanList(Class<Bean> beanType) {
        return new ArrayList<>(context.getBeansOfType(beanType).values());
    }

    /**
     * 获取带有指定注解的对象
     *
     * @param type 指定注解类型
     * @param <A>  注解类型
     * @return 对象实例集合
     */
    public static <A extends Annotation> List<Object> findBeanListWithAnnotation(Class<A> type) {
        return new ArrayList<>(context.getBeansWithAnnotation(type).values());
    }

    /**
     * 初始化上下文
     *
     * @param event 上下文初始化事件
     */
    @Override
    public void onApplicationEvent(ApplicationContextEvent event) {
        ApplicationContextUtils.context = event.getApplicationContext();
    }
}
